package com.bamboo.pistorage.util;

import com.bamboo.core.enums.code.CodeCommonEnum;
import com.bamboo.core.exceptions.BambooException;
import com.bamboo.model.enums.COSBucketEnum;
import com.bamboo.pistorage.domain.TmpAuthEntity;
import com.tencent.cloud.CosStsClient;
import com.tencent.cloud.Policy;
import com.tencent.cloud.Response;
import com.tencent.cloud.Statement;
import com.tencent.cloud.cos.util.Jackson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author chenzh
 * @description 腾讯云对象存储工具类
 * @date 2025/3/26 15:40
 */
public class CosStsUtil {
    static final Logger logger = LoggerFactory.getLogger(CosStsUtil.class);

    /**
     * @param bucketEnum 存储桶信息
     *                   <p>临时密钥生成详情参考<a href="https://cloud.tencent.com/document/product/436/14048">官方文档</a></p>
     * @return TmpAuthEntity
     * @description 获取访问对象存储的临时密钥
     * @date 2025/3/26 15:49
     * @author chenzh
     */
    public static TmpAuthEntity reqTmpAuth(COSBucketEnum bucketEnum) {
        TreeMap<String, Object> config = new TreeMap<>();
        try {
            initConfigWithCredentials(config);
            setBasicConfig(config, bucketEnum);

            Policy policy = createPolicy(bucketEnum);
            config.put("policy", Jackson.toJsonPrettyString(policy));

            Response response = CosStsClient.getCredential(config);
            return buildTmpAuthEntity(response);
        } catch (Exception e) {
            logger.error("获取腾讯云对象存储临时密钥失败。", e);
            throw new BambooException(CodeCommonEnum.SERVER_ERR);
        }
    }

    /**
     * 根据文件名生成对象存储的key
     *
     * @param filename 原始文件名
     * @return 格式为"日期/文件类型/随机文件名.后缀"的对象存储key
     */
    public static String generateObjectKey(String filename) {
        String fileExtension = getFileExtension(filename);
        String dateStr = getCurrentDate();
        String fileType = determineFileType(fileExtension);
        String randomName = generateRandomName();

        return String.format("%s/%s/%s%s", dateStr, fileType, randomName, fileExtension);
    }

    private static void initConfigWithCredentials(TreeMap<String, Object> config) {
        String secretId = System.getenv("secretId");
        String secretKey = System.getenv("secretKey");
        config.put("secretId", secretId);
        config.put("secretKey", secretKey);
    }

    private static void setBasicConfig(TreeMap<String, Object> config, COSBucketEnum bucketEnum) {
        config.put("durationSeconds", 1800);
        config.put("bucket", bucketEnum.getBucketName());
        config.put("region", bucketEnum.getRegion());
    }

    private static Policy createPolicy(COSBucketEnum bucketEnum) {
        Policy policy = new Policy();
        Statement statement = createStatement(bucketEnum);
        policy.addStatement(statement);
        return policy;
    }

    private static Statement createStatement(COSBucketEnum bucketEnum) {
        Statement statement = new Statement();
        statement.setEffect("allow");
        statement.addActions(getAllowedActions());
        statement.addResources(createResources(bucketEnum));
        return statement;
    }

    private static String[] getAllowedActions() {
        return new String[]{
                "name/cos:PutObject",
                "name/cos:InitiateMultipartUpload",
                "name/cos:ListMultipartUploads",
                "name/cos:ListParts",
                "name/cos:UploadPart",
                "name/cos:CompleteMultipartUpload",
                "name/cos:AbortMultipartUpload",
                "name/cos:DeleteObject",
                "name/cos:DeleteObject"
        };
    }

    private static String[] createResources(COSBucketEnum bucketEnum) {
        String cosResource = String.format(
                "qcs::cos:%s:uid/%s:%s/*",
                bucketEnum.getRegion(), bucketEnum.getAppId(), bucketEnum.getBucketName()
        );
        String ciResource = String.format(
                "qcs::ci:%s:uid/%s:bucket/%s/*",
                bucketEnum.getRegion(), bucketEnum.getAppId(), bucketEnum.getBucketName()
        );
        return new String[]{cosResource, ciResource};
    }

    private static TmpAuthEntity buildTmpAuthEntity(Response response) {
        TmpAuthEntity tmpAuth = new TmpAuthEntity();
        tmpAuth.setSecretId(response.credentials.tmpSecretId);
        tmpAuth.setSecretKey(response.credentials.tmpSecretKey);
        tmpAuth.setToken(response.credentials.sessionToken);
        return tmpAuth;
    }

    /**
     * 从文件名中提取文件后缀
     *
     * @param filename 原始文件名
     * @return 文件后缀(包含点)，如".jpg"，如果无后缀则返回空字符串
     */
    private static String getFileExtension(String filename) {
        int dotIndex = filename.lastIndexOf('.');
        return dotIndex > 0 ? filename.substring(dotIndex).toLowerCase() : "";
    }

    /**
     * 获取当前日期字符串
     *
     * @return 格式为"yyyyMMdd"的当前日期字符串
     */
    private static String getCurrentDate() {
        return new SimpleDateFormat("yyyyMMdd").format(new Date());
    }

    /**
     * 根据文件后缀确定文件类型
     *
     * @param fileExtension 文件后缀(包含点)
     * @return 文件类型(" image " / " video " / " document " / " other ")
     */
    private static String determineFileType(String fileExtension) {
        Map<String, String> fileTypeMapping = createFileTypeMapping();
        return fileTypeMapping.getOrDefault(fileExtension, "other");
    }

    /**
     * 创建文件后缀到文件类型的映射关系
     *
     * @return 文件类型映射表
     */
    private static Map<String, String> createFileTypeMapping() {
        return new HashMap<String, String>() {{
            // 图片类型
            put(".jpg", "image");

            put(".jpeg", "image");
            put(".png", "image");
            put(".gif", "image");

            // 视频类型
            put(".mp4", "video");
            put(".mov", "video");
            put(".avi", "video");

            // 文档类型
            put(".pdf", "document");
            put(".doc", "document");
            put(".docx", "document");
            put(".xls", "document");
            put(".xlsx", "document");
        }};
    }

    /**
     * 生成随机文件名
     *
     * @return 32位随机字符串(不含连字符)
     */
    private static String generateRandomName() {
        return UUID.randomUUID().toString().replace("-", "");
    }

}
